// 
//  Author:
//    TachyonXV tachyonxv@psiforum.co.uk
// 
//  Copyright (c) 2011, TachyonXV
// 
//  All rights reserved.
// 
//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in
//       the documentation and/or other materials provided with the distribution.
//     * Neither the name of the [ORGANIZATION] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
// 
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
using DotNetWikiBot;
using NHibernate;
using System.Web;
using System.Text.RegularExpressions;
using System.Globalization;
using Tachbot;
using System;
using System.Collections.Generic;
using System.Net;

namespace CrawlRemix
{
    /// <summary>
    /// Add text loopback
    /// </summary>
    public delegate void AddText(string message);
    
    /// <summary>
	/// 
	/// </summary>
    public sealed class TachBot
	{
        /// <summary>
        /// 
        /// </summary>
        private NHibernate.Cfg.Configuration cfg;
        
        /// <summary>
    	/// ISession Factory
    	/// </summary>
    	private ISessionFactory sessionfact;

        /// <summary>
        /// Occurs when add text.
        /// </summary>
        public static event AddText Add;

        /// <summary>
        /// Allows
        /// </summary>
        private static readonly string[] allows = Defaults.allows;
        
        
        /// <summary>
        /// local site
        /// </summary>
        private Site site;

        /// <summary>
        /// Initializes a new instance of the <see cref="TachBot"/> class.
        /// </summary>
		public TachBot()
		{
            cfg = new NHibernate.Cfg.Configuration();
            cfg.Configure("hibernate.cfg.xml");
            sessionfact = cfg.BuildSessionFactory();
            //NHibernate.Tool.hbm2ddl.SchemaExport ex = new NHibernate.Tool.hbm2ddl.SchemaExport(cfg);
            //ex.Create(true, true);
		}

        /// <summary>
        /// Adds the text.
        /// </summary>
        /// <param name="text">The text.</param>
        public static void AddText(string text)
        {
            Add(text);
        }

        /// <summary>
        /// Gets the do work.
        /// </summary>
        /// <param name="arg">The arg.</param>
        public void GetDoWork(string arg)
        {
            if (site == null)
            {
                site = new Site(Defaults.locsite, Defaults.locname, Defaults.locpass);
            }

            using (ISession session = sessionfact.OpenSession())
            {
                int offSet = 0;
                for (int i = 0; i < 9; i++)
                {
                    var lcount = session.CreateCriteria(typeof(Tachbot.Tbwiki)).SetProjection(NHibernate.Criterion.Projections.Count("Id"))
                        .UniqueResult();
                    int enCount = (int)lcount;
                    if (enCount > 245)
                    {
                        break;
                    }

                    string res = string.Empty;
                    bool all = false;
                    if (arg == "pages")
                    {
                        all = false;
                        res = site.site + site.indexPath + "index.php?title=Special:" + HttpUtility.UrlEncode("WantedPages") +
                            "&limit=250" + "&offset=" + offSet.ToString();
                    }
                    else if (arg == "templates")
                    {
                        all = true;
                        res = site.site + site.indexPath + "index.php?title=Special:" +
                            HttpUtility.UrlEncode("WantedTemplates") + "&limit=" + "100" + "&offset=" + offSet.ToString();
                    }
                    else if (arg == "categories")
                    {
                        all = false;
                        res = site.site + site.indexPath + "index.php?title=Special:" +
                            HttpUtility.UrlEncode("WantedCategories") + "&limit=" + "100" + "&offset=" + offSet.ToString();
                    }

                    string src = site.GetPageHTM(res);
                    Regex linkToPageRE2 = null;
                    if ((arg == "pages") || (arg == "templates"))
                    {
                        linkToPageRE2 = new Regex(@"<a href=""[^""]*?"" class=""new"" " +
                            @"title=""([^""]+?)"">[^""]+?</a> [^""]+<a href=""[^""]*?"" title=""[^""]+?"">([0-9]+)");
                    }
                    else if (arg == "categories")
                    {
                        linkToPageRE2 = new Regex(@"<a href=""[^""]*?"" class=""new"" " +
                            @"title=""([^""]+?)"">[^""]+?</a> [^""]+([0-9]+)");
                    }

                    MatchCollection matches = null;
                    if (linkToPageRE2 != null)
                    {
                        matches = linkToPageRE2.Matches(src);
                    }

                    if ((matches != null) && (matches.Count > 0))
                    {
                        foreach (Match match in matches)
                        {
                            string theTitle = match.Groups[1].Value;
                            string pageTitle = theTitle.Substring(0, theTitle.Length - 22);
                            string count = match.Groups[2].Value;
                            if (pageTitle.Contains(":"))
                            {
                                string[] split = pageTitle.Split(new char[] { ':' });
                                string sp0 = split[0];
                                if ((sp0 == "Template") ||
                                    (sp0 == "Help") ||
                                    (sp0 == "Category")/* ||
                                (sp0 == "WP") ||
                                (sp0 == "Wikipedia") ||
                                (sp0 == "Type")*/)
                                {
                                    int theCount = int.Parse(count, CultureInfo.InvariantCulture);
                                    InsertIt(pageTitle, theCount, true);
                                }
                                else if ((sp0 == "Type") ||
                                    (sp0 == "File"))
                                {
                                    TachBot.Add("add " + pageTitle + "\r\n");
                                }
                                else
                                {
                                    TachBot.Add("prefix " + split[0] + "\r\n");
                                }
                            }
                            else
                            {
                                int theCount = int.Parse(count, CultureInfo.InvariantCulture);
                                InsertIt(pageTitle, theCount, all);
                            }
                        }
                    }
                    else
                    {
                        break;
                    }

                    if (arg == "pages")
                    {
                        offSet += 250;
                    }
                    else
                    {
                        offSet += 100;
                    }

                    TachBot.Add("Done " + i.ToString() + "\r\n");
                }
            }
		}

        /// <summary>
        /// Inerts it.
        /// </summary>
        /// <param name="pageTitle">The page title.</param>
        /// <param name="theCount">The count.</param>
        /// <param name="allow">auto allow</param>
        private void InsertIt(string pageTitle, int theCount, bool allow)
        {
            using (ISession session = sessionfact.OpenSession())
            {
                ITransaction transaction = session.BeginTransaction();
                Tbwiki tbw = new Tbwiki();
                tbw.Count = theCount;
                if (allow)
                {
                    tbw.Enabled = true;
                }
                else
                {
                    tbw.Enabled = Allowed(pageTitle);
                }

                tbw.Page = pageTitle;
                session.Save(tbw);
                /*try
                {*/
                transaction.Commit();
                /*}
                catch (NHibernate.Exceptions.GenericADOException)
                {
                    transaction.Rollback();
                    this.Add("Insert it:" + tbw.Page + "\r\n");
                    fail = true;
                }
                finally
                {
                    transaction.Dispose();
                }*/
            }
        }

        /// <summary>
        /// Alloweds the specified page title.
        /// </summary>
        /// <param name="pageTitle">The page title.</param>
        /// <returns></returns>
        private bool Allowed(string pageTitle)
        {
            bool ok = true;
            foreach (string allow in allows)
            {
                if (pageTitle.Contains(allow))
                {
                    ok = false;
                    break;
                }
            }

            return ok;
        }

        /// <summary>
        /// Get pages
        /// </summary>
        internal void ParsePages()
        {
            if (site == null)
            {
                site = new Site(Defaults.locsite, Defaults.locname, Defaults.locpass);
            }
            
            Site wpsite = new Site(Defaults.wpsite, Defaults.wpname, Defaults.wppass);
            using (ISession session = sessionfact.OpenSession())
            {
                ICriteria crit = session.CreateCriteria<Tbwiki>();
                IList<Tbwiki> iltbw = crit.List<Tbwiki>();
                foreach (Tbwiki tbwik in iltbw)
                {
                    Page page = new Page(site, tbwik.Page);
                    page.Load();
                    if (tbwik.Enabled)
                    {
                        Page wpage = new Page(wpsite, tbwik.Page);
                        wpage.Load();
                        if (wpage.Exists())
                        {
                            page.text = wpage.text;
                        }
                        else
                        {
                            page.text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                        }
                    }
                    else
                    {
                        page.text = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
                    }

                    try
                    {
                        page.Save("Bot edit", true);
                    }
                    catch (WebException)
                    {
                    }

                    using (ITransaction trans = session.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
                    {
                        session.Delete(tbwik);
                        trans.Commit();
                    }
                }
            }
        }

        /// <summary>
        /// Pages
        /// </summary>
        /// <returns>Pages</returns>
        internal void ResetPages()
        {
            using (ISession session = sessionfact.OpenSession())
            {
                using (ITransaction trans = session.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
                {
                    IQuery q = session.CreateQuery("delete Tbwiki t");
                    int num = q.ExecuteUpdate();
                    trans.Commit();
                }
            }
        }

        /// <summary>
        /// Pages
        /// </summary>
        /// <returns>Pages</returns>
        internal IList<Tbwiki> Pages()
        {
            using (ISession session = sessionfact.OpenSession())
            {
                ICriteria crit = session.CreateCriteria<Tbwiki>();
                /*try
                {*/
                IList<Tbwiki> iltbw = crit.List<Tbwiki>();
                return iltbw;
                /*}
                catch (Exception eek)
                {
                    this.Add("Pages" + eek.Message);
                    return null;
                }*/
            }
        }
    }
}
